﻿using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DOW = DocumentFormat.OpenXml.Wordprocessing;
using DRAW = DocumentFormat.OpenXml.Drawing;
using DWP = DocumentFormat.OpenXml.Drawing.Wordprocessing;
using PIC = DocumentFormat.OpenXml.Drawing.Pictures;
using SD = System.Drawing;
using Ap = DocumentFormat.OpenXml.ExtendedProperties;

namespace Tatibah
{
#pragma warning disable 1591
    public enum enumUnits
    {
        inches,
        cm,
        points
#pragma warning restore 1591
    }
    /// <summary>
    /// The Word Document being created/edited
    /// </summary>
    public class Document: BaseSectionFormatting, IDisposable
    {

        const string GraphicDataUri = "http://schemas.openxmlformats.org/drawingml/2006/picture";
        const char ROWSPANSTART = 'S';
        const char ROWSPANCONTINUE = 'C';
        const char COLUMNONLYSPANNED = 'O';
        const char CELLSPANNEDIGNORE = 'I';
        
        // Internally WordProcessingMl uses units of DXA and EMU.
        // The following constant are used to convert user units (inches, cm, points) to WordProcessingMl units.
        const double CMtoDxa = 567;
        const double PointToDxa = 20;
        const double InchToDxa = 1440;
        const double CMtoEmus = 360000;
        const double PointToEmus = 1270;
        const double InchToEmus = 914400;

        private const string ID = "tId";
        private const string UNIQUEID = "document";
  
        /// <summary>
        /// The cuurent WordProcessingMl paragraph usually the one just added
        /// </summary>
        public DocumentFormat.OpenXml.Wordprocessing.Paragraph CurrentParagraph = null;

        /// <summary>
        /// The cuurent WordProcessingMl Table usually the one just added
        /// </summary>
        public DocumentFormat.OpenXml.Wordprocessing.Table CurrentTable = null;

        /// <summary>
        /// The WordProcessingMl Body of the Document
        /// </summary>
        public DOW.Body CurrentBody = null;

        /// <summary>
        /// When set Closing or Disposing of the document will cause it to be saved
        /// </summary>
        public bool Autosave = true;

        static int IdCount = 1;
        static int hexCount = 0x12345678;  // an eight octet number
        
        private WordprocessingDocument document = null;
        private MainDocumentPart mainPart = null;
        private DocumentSettingsPart documentSettingsPart = null;
        private StyleDefinitionsPart styleDefinitionsPart = null;
        private NumberingDefinitionsPart numberingDefinitionPart = null;
        private ImagePart imagePart = null;
        private HeaderPart headerPartFirstPage = null;
        private HeaderPart headerPartEvenPages = null;
        private HeaderPart headerPartOddPages = null;
        private FooterPart footerPartFirstPage = null;
        private FooterPart footerPartEvenPages = null;
        private FooterPart footerPartOddPages = null;
        private OpenXmlPart currentHeaderOrFooterPart = null; // Base class of HeaderPart and FooterPart used for Image processing
        private DOW.Settings documentSetting = null;
        private DOW.SectionProperties documentSectionProperties = new DOW.SectionProperties();
        private List<DOW.AbstractNum> abstractNumberList = null;
        private DOW.AbstractNum w_abstractNum = null;
        private DOW.Numbering w_numbering = null;
        private DOW.NumberingProperties w_numPr = null;  // Used with numbered lists (and bullet list)
        private DOW.TitlePage wtitlePg = null;
        DOW.EvenAndOddHeaders w_evenAndOddHeaders = null;

        private bool saved = false;
        private bool closed = false;
        private bool disposed = false;

        // merge map ensures that only rectangler sections of Tables are merged and merged areas do NOT clash.
        // character meanings are S (ROWSPANSTART) = startRowSpan, C (COLSPANNED) = ColumnHasBeen Spanned, O (COLONLYSPANNED) means only the column is spanned /0 means ignore
        private char[,] mergeMap = null;
        // used to readjust the columnspan or row span of a Cell if it goes outside the table or clashed with previously merged cells
        private int columnsToSpan = 0; 
        private int rowsToSpan = 0;
        private Dictionary<string, int> imageInPart = new Dictionary<string, int>();
        /// <summary>
        /// Create a new Word Document
        /// </summary>
        /// <param name="FileName"></param>
        public Document(string FileName)
        {
            // Create a document.
            try
            {
                document = WordprocessingDocument.Create(FileName, WordprocessingDocumentType.Document);
            }
            catch (Exception e)
            {
                throw new Exception("Cannot create Document. " + e.Message);
            }
       
            // Create a main document part and add it to the document.
            mainPart = document.AddMainDocumentPart();
            mainPart.Document = new DocumentFormat.OpenXml.Wordprocessing.Document();

            CurrentBody = new DOW.Body();

           

        }


        /// <summary>
        /// Create a new Document and Append the included list of items
        /// </summary>
        /// <param name="FileName"></param>
        /// <param name="MultipleItems"></param>
        public Document( string FileName, params iDocumentAppend[]  MultipleItems)
        {
            
            try
            {
                // Create a document.
                document = WordprocessingDocument.Create(FileName, WordprocessingDocumentType.Document);
            }
            catch (Exception e)
            {
                throw new Exception("Cannot create Document. " + e.Message);
            }

            // Create a main document part and add it to the document.
            mainPart = document.AddMainDocumentPart();
            mainPart.Document = new DocumentFormat.OpenXml.Wordprocessing.Document();
            CurrentBody = new DOW.Body();

            foreach (iDocumentAppend item in MultipleItems)
            {
                if (item.GetType() == typeof(Paragraph))
                    Append( (Paragraph)item);
                else if (item.GetType() == typeof(Table))
                    Append( (Table) item);
                else
                    Append( (NumberedList) item);
            }
        }


        /// <summary>
        /// Dispose of the Document
        /// </summary>
        public void Dispose()
        {
            // Close the document before we dispose of it, If autosave on Close will Save the Document
            if (!closed) Close();
            
            // If Close called before Dispose it will Dispose of the Document
            if (!disposed) document.Dispose();
            disposed = true;
            
        }

       

          /// <summary>
          /// Saves the Style in the Document, done automatically for user defined Styles
          /// </summary>
          /// <param name="ThisStyle"></param>
        public void SaveStyle(Style ThisStyle)
        {
            if (ThisStyle.Id == null || ThisStyle.Id == "")
                ThisStyle.ReservedI(GetNextId());  // Must have a style id, must be unique

            // If the Styles part does not exist, add it and then add the style.
            if (styleDefinitionsPart == null)
            {
                styleDefinitionsPart = document.MainDocumentPart.AddNewPart<StyleDefinitionsPart>();
                DOW.Styles w_styles = new DOW.Styles();
                w_styles.Save(styleDefinitionsPart);
            }
            
            // Get access to the root element of the styles part.
            DOW.Styles styles = document.MainDocumentPart.StyleDefinitionsPart.Styles;
              

            // Create a new paragraph style and specify some of the properties.
            DocumentFormat.OpenXml.Wordprocessing.Style w_style = new DocumentFormat.OpenXml.Wordprocessing.Style()
            {
                Type = DOW.StyleValues.Paragraph,
                StyleId = ThisStyle.Id,
                CustomStyle = true
            };

            DOW.StyleName w_name = new DOW.StyleName() { Val = ThisStyle.Name };
            DOW.BasedOn w_basedOn = new DOW.BasedOn() { Val = ThisStyle.BasedOn };
            w_style.Append(w_name);
            w_style.Append(w_basedOn);
           
            // Add any Paragraph Formatting
            DOW.StyleParagraphProperties w_pPr = new DOW.StyleParagraphProperties();
            AddFormatting(w_pPr, ThisStyle);
            w_style.Append(w_pPr);
            
            // Create the StyleRunProperties object and specify some of the run properties.
            DOW.StyleRunProperties w_rPr = new DOW.StyleRunProperties();
            AddFormatting(w_rPr, (BaseTextFormatting) ThisStyle);
            w_style.Append(w_rPr);

            // Add the style to the styles part.
            styles.Append(w_style);
        }

       

        private void SaveNumberedListFormat(NumberedList ThisListParagraph)
        {
            DOW.Level w_lvl = null;
            if (numberingDefinitionPart == null)
            {
                string numberPartId = GetNextId();
                numberingDefinitionPart = mainPart.AddNewPart<NumberingDefinitionsPart>(numberPartId);
            }
            if (abstractNumberList == null) abstractNumberList = new List<DOW.AbstractNum>();
            w_numbering = new DOW.Numbering();

            // Add a new Abstract Number
            w_abstractNum = new DOW.AbstractNum(
                   new DOW.Nsid() { Val = new HexBinaryValue(GetNextHexId()) }, // Always start a new List so use zero as base
                   new DOW.MultiLevelType() { Val = DOW.MultiLevelValues.HybridMultilevel },
                   new DOW.TemplateCode() { Val = new HexBinaryValue(GetNextHexId()) }
              ) { AbstractNumberId = ThisListParagraph.LevelId };


            switch (ThisListParagraph.ListType)
            {
                case enumDocumentListType.Bullet:
                    w_lvl = new DOW.Level() { LevelIndex = new Int32Value(0), TemplateCode = new HexBinaryValue(GetNextHexId()) };
                    w_lvl.Append(new DOW.StartNumberingValue() { Val = ThisListParagraph.Start });
                    w_lvl.Append(new DOW.NumberingFormat() { Val = DOW.NumberFormatValues.Bullet });
                    w_lvl.Append(new DOW.LevelText() { Val = ThisListParagraph.BulletChar });
                    w_lvl.Append(new DOW.LevelJustification() { Val = DOW.LevelJustificationValues.Left });
                    w_lvl.Append(new DOW.ParagraphProperties(
                        new DOW.Indentation() { Left = new StringValue(ThisListParagraph.LeftIndent.ToString()), Hanging = new StringValue(ThisListParagraph.Hanging.ToString()) }
                    ));
                    w_lvl.Append(new DOW.RunProperties(new DOW.RunFonts() { Ascii = ThisListParagraph.BulletFont }));
                    break;

                case enumDocumentListType.Decimal:
                    w_lvl = new DOW.Level() { LevelIndex = new Int32Value(0), TemplateCode = new HexBinaryValue(GetNextHexId()) };
                    w_lvl.Append(new DOW.StartNumberingValue() { Val = ThisListParagraph.Start });
                    w_lvl.Append(new DOW.NumberingFormat() { Val = DOW.NumberFormatValues.Decimal });
                    w_lvl.Append(new DOW.LevelText() { Val = "%1." });
                    w_lvl.Append(new DOW.LevelJustification() { Val = DOW.LevelJustificationValues.Left });
                    w_lvl.Append(new DOW.ParagraphProperties(
                         new DOW.Indentation() { Left = new StringValue(ThisListParagraph.LeftIndent.ToString()), Hanging = new StringValue(ThisListParagraph.Hanging.ToString()) }
                    ));
                    break;

                case enumDocumentListType.LowerRoman:
                    w_lvl = new DOW.Level() { LevelIndex = new Int32Value(0), TemplateCode = new HexBinaryValue(GetNextHexId()) };
                    w_lvl.Append(new DOW.StartNumberingValue() { Val = ThisListParagraph.Start });
                    w_lvl.Append(new DOW.NumberingFormat() { Val = DOW.NumberFormatValues.LowerRoman });
                    w_lvl.Append(new DOW.LevelText() { Val = "%1." });
                    w_lvl.Append(new DOW.LevelJustification() { Val = DOW.LevelJustificationValues.Left });
                    w_lvl.Append(new DOW.ParagraphProperties(
                         new DOW.Indentation() { Left = new StringValue(ThisListParagraph.LeftIndent.ToString()), Hanging = new StringValue(ThisListParagraph.Hanging.ToString()) }
                    ));
                    break;

                case enumDocumentListType.LowerLetter:
                    w_lvl = new DOW.Level() { LevelIndex = new Int32Value(0), TemplateCode = new HexBinaryValue(GetNextHexId()), Tentative = new OnOffValue(true) };
                    w_lvl.Append(new DOW.StartNumberingValue() { Val = ThisListParagraph.Start });
                    w_lvl.Append(new DOW.NumberingFormat() { Val = DOW.NumberFormatValues.LowerLetter });
                    w_lvl.Append(new DOW.LevelText() { Val = "%1." });
                    w_lvl.Append(new DOW.LevelJustification() { Val = DOW.LevelJustificationValues.Left });
                    w_lvl.Append(new DOW.ParagraphProperties(
                        new DOW.Indentation() { Left = new StringValue(ThisListParagraph.LeftIndent.ToString()), Hanging = new StringValue(ThisListParagraph.Hanging.ToString()) }
                    ));
                    break;

                case enumDocumentListType.UpperRoman:
                    w_lvl = new DOW.Level() { LevelIndex = new Int32Value(0), TemplateCode = new HexBinaryValue(GetNextHexId()) };
                    w_lvl.Append(new DOW.StartNumberingValue() { Val = ThisListParagraph.Start });
                    w_lvl.Append(new DOW.NumberingFormat() { Val = DOW.NumberFormatValues.UpperRoman });
                    w_lvl.Append(new DOW.LevelText() { Val = "%1." });
                    w_lvl.Append(new DOW.LevelJustification() { Val = DOW.LevelJustificationValues.Left });
                    w_lvl.Append(new DOW.ParagraphProperties(
                         new DOW.Indentation() { Left = new StringValue(ThisListParagraph.LeftIndent.ToString()), Hanging = new StringValue(ThisListParagraph.Hanging.ToString()) }
                    ));
                    break;

                case enumDocumentListType.UpperLetter:
                    w_lvl = new DOW.Level() { LevelIndex = new Int32Value(0), TemplateCode = new HexBinaryValue(GetNextHexId()), Tentative = new OnOffValue(true) };
                    w_lvl.Append(new DOW.StartNumberingValue() { Val = ThisListParagraph.Start });
                    w_lvl.Append(new DOW.NumberingFormat() { Val = DOW.NumberFormatValues.UpperLetter });
                    w_lvl.Append(new DOW.LevelText() { Val = "%1." });
                    w_lvl.Append(new DOW.LevelJustification() { Val = DOW.LevelJustificationValues.Left });
                    w_lvl.Append(new DOW.ParagraphProperties(
                        new DOW.Indentation() { Left = new StringValue(ThisListParagraph.LeftIndent.ToString()), Hanging = new StringValue(ThisListParagraph.Hanging.ToString()) }
                    ));
                    break;
                default:
                    break;
            }
            w_abstractNum.Append(w_lvl);
            abstractNumberList.Add(w_abstractNum);
        }

       

        /// <summary>
        /// Add a simple Header and Footer. Header and Footer will appear on every page
        /// </summary>
        /// <param name="ThisHeader"></param>
        /// <param name="ThisFooter"></param>
        public void Append(Header ThisHeader, Footer ThisFooter)
        {
            bool headerOrFooter = false;
            string headerId = GetNextId();
            string footerId = GetNextId();
            if (ThisHeader != null)
            {
                headerOrFooter = true;
                headerPartFirstPage = mainPart.AddNewPart<HeaderPart>(headerId);

                // Add the header to the document section properties
                DocumentFormat.OpenXml.Packaging.HeaderPart firstHeader = document.MainDocumentPart.HeaderParts.ElementAt(0);
                currentHeaderOrFooterPart = firstHeader;

                firstHeader.Header = new DocumentFormat.OpenXml.Wordprocessing.Header();
                
                AddHeaderFooterContent(ThisHeader, firstHeader.Header, this);
                
            }


            if (ThisFooter != null)
            {
                headerOrFooter = true;
                footerPartFirstPage = mainPart.AddNewPart<FooterPart>(footerId);
                currentHeaderOrFooterPart = footerPartFirstPage;

                DocumentFormat.OpenXml.Packaging.FooterPart firstFooter = document.MainDocumentPart.FooterParts.ElementAt(0);

                firstFooter.Footer = new DocumentFormat.OpenXml.Wordprocessing.Footer();

                AddHeaderFooterContent(ThisFooter, firstFooter.Footer, this);

              
            }
            currentHeaderOrFooterPart = null;
            if (headerOrFooter)
            {
                if (ThisHeader != null)
                {
                    DOW.HeaderReference wheaderReference = new DOW.HeaderReference();
                    wheaderReference.Id = headerId;
                    wheaderReference.Type = DOW.HeaderFooterValues.Default;
                    documentSectionProperties.Append(wheaderReference);
                }

                if (ThisFooter != null)
                {
                    DOW.FooterReference wfooterReference = new DOW.FooterReference();
                    wfooterReference.Id = footerId;
                    wfooterReference.Type = DOW.HeaderFooterValues.Default;
                    documentSectionProperties.Append(wfooterReference);
                }
            }
        
        }


        /// <summary>
        /// Append multiple Paragraphs, Tables and/or NumberedList
        /// </summary>
        /// <param name="MultipleItems"></param>
        public void Append(params iDocumentAppend[] MultipleItems)
        {
           
          
            foreach (iDocumentAppend item in MultipleItems)
            {
                // Must be null if it isn't a numbered list
                w_numPr = null;

                if (item.GetType() == typeof(Paragraph))
                {
                    CurrentParagraph = CreateWordParagraph((Paragraph) item, UNIQUEID);
                    // Add the content into the document.
                    CurrentBody.Append(CurrentParagraph);
                }
                else if (item.GetType() == typeof(Table))
                {
                    CurrentTable = CreateWordTable((Table)item, UNIQUEID);
                    CurrentBody.Append(CurrentTable);
                }
                else
                {
                    SaveNumberedListFormat((NumberedList)item);

                    foreach (Paragraph p in ((NumberedList)item).listOfParagraphs)
                    {
                        // Step 1 Create the numering Property it will be added to the Paragraph
                        w_numPr = new DOW.NumberingProperties(
                              new DOW.NumberingLevelReference() { Val = 0 },
                              new DOW.NumberingId() { Val = ((NumberedList)item).LevelId + 1 }
                              );

                        // Step 2. CreateWordParagraph will evalute w_numPr and if not null then insert it in the approriate place
                        DocumentFormat.OpenXml.Wordprocessing.Paragraph wordPara = CreateWordParagraph(p, UNIQUEID);
                      

                        // Step 3 Add the paragraph to the document body
                        CurrentBody.Append(wordPara);
                    }
                    w_numPr = null;
                } 
            }   
        }

        /// <summary>
        /// Append First page Header and Footer and every other page Header and Footer.
        /// </summary>
        /// <param name="FirstPageHeader">Appears on first page only</param>
        /// <param name="EvenPagesHeader">Appears on every page but the first</param>
        /// <param name="FirstPageFooter">Appears on first page only</param>
        /// <param name="EvenPagesFooter">Appears on every page but the first</param>
        public void Append(Header FirstPageHeader, Header EvenPagesHeader, Footer FirstPageFooter, Footer EvenPagesFooter)
        {
            bool headerOrFooter = false;
            string firstPageHeaderId = GetNextId();
            string otherPagesHeaderId = GetNextId();
            string firstPageFooterId = GetNextId();
            string otherPagesFooterId = GetNextId();
            int headerIndex = 0;
            int footerIndex = 0;

            wtitlePg = null;
           
            if (FirstPageHeader != null)
            {
                headerOrFooter = true;
                headerPartFirstPage = mainPart.AddNewPart<HeaderPart>(firstPageHeaderId);
                currentHeaderOrFooterPart = headerPartFirstPage;

                DocumentFormat.OpenXml.Packaging.HeaderPart firstHeader = document.MainDocumentPart.HeaderParts.ElementAt(headerIndex++);

                firstHeader.Header = new DocumentFormat.OpenXml.Wordprocessing.Header();

                AddHeaderFooterContent(FirstPageHeader, firstHeader.Header, this);

            }

            if (EvenPagesHeader != null)
            {
                headerOrFooter = true;
                headerPartEvenPages = mainPart.AddNewPart<HeaderPart>(otherPagesHeaderId);
                currentHeaderOrFooterPart = headerPartEvenPages;

                DocumentFormat.OpenXml.Packaging.HeaderPart otherPagesHeader = document.MainDocumentPart.HeaderParts.ElementAt(headerIndex++);

                otherPagesHeader.Header = new DocumentFormat.OpenXml.Wordprocessing.Header();

                AddHeaderFooterContent(EvenPagesHeader, otherPagesHeader.Header, this);

            }

            if (FirstPageFooter != null)
            {
                headerOrFooter = true;
                footerPartFirstPage = mainPart.AddNewPart<FooterPart>(firstPageFooterId);
                currentHeaderOrFooterPart = footerPartFirstPage;

                DocumentFormat.OpenXml.Packaging.FooterPart firstFooter = document.MainDocumentPart.FooterParts.ElementAt(footerIndex++);

                firstFooter.Footer = new DocumentFormat.OpenXml.Wordprocessing.Footer();
               
                AddHeaderFooterContent(FirstPageFooter, firstFooter.Footer, this);

            }

            if (EvenPagesFooter != null)
            {
                headerOrFooter = true;
                footerPartEvenPages = mainPart.AddNewPart<FooterPart>(otherPagesFooterId);
                currentHeaderOrFooterPart = footerPartEvenPages;

                DocumentFormat.OpenXml.Packaging.FooterPart otherPagesFooter = document.MainDocumentPart.FooterParts.ElementAt(footerIndex++);

                otherPagesFooter.Footer = new DocumentFormat.OpenXml.Wordprocessing.Footer();

                AddHeaderFooterContent(EvenPagesFooter, otherPagesFooter.Footer, this);

            }
            currentHeaderOrFooterPart = null;
            if (headerOrFooter)
            {

                if (FirstPageHeader != null)
                {
                    DOW.HeaderReference w_headerReference = new DOW.HeaderReference();
                    w_headerReference.Id = firstPageHeaderId;
                    w_headerReference.Type = DOW.HeaderFooterValues.First;
                    documentSectionProperties.Append(w_headerReference);
                }

                if (EvenPagesHeader != null)
                {
                    DOW.HeaderReference w_headerReference = new DOW.HeaderReference();
                    w_headerReference.Id = otherPagesHeaderId;
                    w_headerReference.Type = DOW.HeaderFooterValues.Default;
                    documentSectionProperties.Append(w_headerReference);
                }

                if (FirstPageFooter != null)
                {
                    DOW.FooterReference wfooterReference = new DOW.FooterReference();
                    wfooterReference.Id = firstPageFooterId;
                    wfooterReference.Type = DOW.HeaderFooterValues.First;
                    documentSectionProperties.Append(wfooterReference);
                }

                if (EvenPagesFooter != null)
                {
                    DOW.FooterReference wfooterReference = new DOW.FooterReference();
                    wfooterReference.Id = otherPagesFooterId;
                    wfooterReference.Type = DOW.HeaderFooterValues.Default;
                    documentSectionProperties.Append(wfooterReference);
                }

                wtitlePg = new DOW.TitlePage();
            }
        
        }
 
        /// <summary>
        /// Append First page Header and Footer, Even Page Headers and Footers and Odd page Headers and Footers
        /// </summary>
        /// <param name="FirstPageHeader"></param>
        /// <param name="EvenPagesHeader"></param>
        /// <param name="OddPagesHeader"></param>
        /// <param name="FirstPageFooter"></param>
        /// <param name="EvenPagesFooter"></param>
        /// <param name="OddPagesFooter"></param>
        public void Append(Header FirstPageHeader, Header EvenPagesHeader, Header OddPagesHeader, Footer FirstPageFooter, Footer EvenPagesFooter, Footer OddPagesFooter)
        {
            bool headerOrFooter = false;
            string firstPageHeaderId = GetNextId();
            string evenPagesHeaderId = GetNextId();
            string oddPagesHeaderId = GetNextId();
            string firstPageFooterId = GetNextId();
            string evenPagesFooterId = GetNextId();
            string oddPagesFooterId = GetNextId();
            int headerIndex = 0;
            int footerIndex = 0;

            wtitlePg = null;
            w_evenAndOddHeaders = null;

            if (FirstPageHeader != null)
            {
                headerOrFooter = true;
                headerPartFirstPage = mainPart.AddNewPart<HeaderPart>(firstPageHeaderId);
                currentHeaderOrFooterPart = headerPartFirstPage;

                DocumentFormat.OpenXml.Packaging.HeaderPart firstHeader = document.MainDocumentPart.HeaderParts.ElementAt(headerIndex++);

                firstHeader.Header = new DocumentFormat.OpenXml.Wordprocessing.Header();

                AddHeaderFooterContent(FirstPageHeader, firstHeader.Header, this);

            }

            if (EvenPagesHeader != null)
            {
                headerOrFooter = true;
                headerPartEvenPages = mainPart.AddNewPart<HeaderPart>(evenPagesHeaderId);
                currentHeaderOrFooterPart = headerPartEvenPages;

                DocumentFormat.OpenXml.Packaging.HeaderPart evenPagesHeader = document.MainDocumentPart.HeaderParts.ElementAt(headerIndex++);

                evenPagesHeader.Header = new DocumentFormat.OpenXml.Wordprocessing.Header();

                AddHeaderFooterContent(EvenPagesHeader, evenPagesHeader.Header, this);

            }

            if (OddPagesHeader != null)
            {
                headerOrFooter = true;
                headerPartOddPages = mainPart.AddNewPart<HeaderPart>(oddPagesHeaderId);
                currentHeaderOrFooterPart = headerPartOddPages;

                DocumentFormat.OpenXml.Packaging.HeaderPart oddPagesHeader = document.MainDocumentPart.HeaderParts.ElementAt(headerIndex++);

                oddPagesHeader.Header = new DocumentFormat.OpenXml.Wordprocessing.Header();

                AddHeaderFooterContent(OddPagesHeader, oddPagesHeader.Header, this);

            }

            if (FirstPageFooter != null)
            {
                headerOrFooter = true;
                footerPartFirstPage = mainPart.AddNewPart<FooterPart>(firstPageFooterId);
                currentHeaderOrFooterPart = footerPartFirstPage;

                DocumentFormat.OpenXml.Packaging.FooterPart firstFooter = document.MainDocumentPart.FooterParts.ElementAt(footerIndex++);

                firstFooter.Footer = new DocumentFormat.OpenXml.Wordprocessing.Footer();

                AddHeaderFooterContent(FirstPageFooter, firstFooter.Footer, this);

            }

            if (EvenPagesFooter != null)
            {
                headerOrFooter = true;
                footerPartEvenPages = mainPart.AddNewPart<FooterPart>(evenPagesFooterId);
                currentHeaderOrFooterPart = footerPartEvenPages;

                DocumentFormat.OpenXml.Packaging.FooterPart evenPagesFooter = document.MainDocumentPart.FooterParts.ElementAt(footerIndex++);

                evenPagesFooter.Footer = new DocumentFormat.OpenXml.Wordprocessing.Footer();

                AddHeaderFooterContent(EvenPagesFooter, evenPagesFooter.Footer, this);

            }

            if (OddPagesFooter != null)
            {
                headerOrFooter = true;
                footerPartOddPages = mainPart.AddNewPart<FooterPart>(oddPagesFooterId);
                currentHeaderOrFooterPart = footerPartOddPages;

                DocumentFormat.OpenXml.Packaging.FooterPart oddPagesFooter = document.MainDocumentPart.FooterParts.ElementAt(footerIndex++);

                oddPagesFooter.Footer = new DocumentFormat.OpenXml.Wordprocessing.Footer();

                AddHeaderFooterContent(OddPagesFooter, oddPagesFooter.Footer, this);

            }

            currentHeaderOrFooterPart = null;
            if (headerOrFooter)
            {
                if (EvenPagesHeader != null)
                {
                    DOW.HeaderReference w_headerReference = new DOW.HeaderReference();
                    w_headerReference.Id = evenPagesHeaderId;
                    w_headerReference.Type = DOW.HeaderFooterValues.Even;
                    documentSectionProperties.Append(w_headerReference);
                }

                if (OddPagesHeader != null)
                {
                    DOW.HeaderReference w_headerReference = new DOW.HeaderReference();
                    w_headerReference.Id = oddPagesHeaderId;
                    w_headerReference.Type = DOW.HeaderFooterValues.Default;
                    documentSectionProperties.Append(w_headerReference);
                }

               
                if (EvenPagesFooter != null)
                {
                    DOW.FooterReference wfooterReference = new DOW.FooterReference();
                    wfooterReference.Id = evenPagesFooterId;
                    wfooterReference.Type = DOW.HeaderFooterValues.Even;
                    documentSectionProperties.Append(wfooterReference);
                }

                if (OddPagesFooter != null)
                {
                    DOW.FooterReference wfooterReference = new DOW.FooterReference();
                    wfooterReference.Id = oddPagesFooterId;
                    wfooterReference.Type = DOW.HeaderFooterValues.Default;
                    documentSectionProperties.Append(wfooterReference);
                }

                if (FirstPageHeader != null)
                {
                    DOW.HeaderReference w_headerReference = new DOW.HeaderReference();
                    w_headerReference.Id = firstPageHeaderId;
                    w_headerReference.Type = DOW.HeaderFooterValues.First;
                    documentSectionProperties.Append(w_headerReference);
                }

                if (FirstPageFooter != null)
                {
                    DOW.FooterReference wfooterReference = new DOW.FooterReference();
                    wfooterReference.Id = firstPageFooterId;
                    wfooterReference.Type = DOW.HeaderFooterValues.First;
                    documentSectionProperties.Append(wfooterReference);
                }


                wtitlePg = new DOW.TitlePage();
                w_evenAndOddHeaders = new DOW.EvenAndOddHeaders();
            }

        }


        /// <summary>
        /// Create a OOXML Paragraph from a Tatibah.Paragraph it could then be added to the CurrentBody. When serialized the qualified name is w:p
        /// </summary>
        /// <param name="ThisParagraph"></param>
        /// <returns></returns>
        public DocumentFormat.OpenXml.Wordprocessing.Paragraph CreateWordParagraph(Paragraph ThisParagraph, string UniqueId)
        {
           
            DOW.Run run = null;
            DOW.RunProperties runProperties = null;
            DOW.ParagraphProperties paragraphProperties = new DOW.ParagraphProperties();  // We always add paragraph properties, may be needed later e.g. Bullet or Numbered List
            DocumentFormat.OpenXml.Wordprocessing.Paragraph wordParagraph = new DocumentFormat.OpenXml.Wordprocessing.Paragraph();

            if (ThisParagraph.InternalRuns != null)
            {
                foreach (Run intRun in ThisParagraph.InternalRuns)
                {
                   
                    switch (intRun.RunType)
                    {
                        case enumRunType.Standard: 
                        case   enumRunType.FieldCode:

                            runProperties = new DOW.RunProperties();
                            AddFormatting(runProperties, intRun, ThisParagraph);
 
                            if (intRun.RunType == enumRunType.Standard)
                            {
                                run = new DOW.Run();
                                run.Append(runProperties); // May not be any properties but we add them anyway
                                DOW.Text text = new DOW.Text(intRun.Text);
                                if (intRun.PreserveSpacing)
                                    text.Space = SpaceProcessingModeValues.Preserve; // preserve space

                                run.Append(text);
                               
                                runProperties = null; // set to null for next iteration

                                wordParagraph.Append(run);
                            }
                            else  // Must be a field code
                            {

                                // Add the Begin Field Char
                                run = new DOW.Run();
                                run.Append((DOW.RunProperties)runProperties.Clone());
                                DOW.FieldChar wfldChar = new DOW.FieldChar();
                                wfldChar.FieldCharType = DOW.FieldCharValues.Begin;
                                run.Append(wfldChar);
                                wordParagraph.Append(run);

                                // Add The field Code
                                run = new DOW.Run();
                                run.Append((DOW.RunProperties)runProperties.Clone());
                                DOW.FieldCode winstrText = new DOW.FieldCode();
                                winstrText.Text = intRun.Text;
                                winstrText.Space = SpaceProcessingModeValues.Preserve;
                                run.Append(winstrText);
                                wordParagraph.Append(run);

                                // Add the Seperate Field Char
                                run = new DOW.Run();
                                run.Append((DOW.RunProperties)runProperties.Clone());
                                wfldChar = new DOW.FieldChar();
                                wfldChar.FieldCharType = DOW.FieldCharValues.Separate;
                                run.Append(wfldChar);
                                wordParagraph.Append(run);

                                // Add the End Field Char
                                run = new DOW.Run();
                                run.Append((DOW.RunProperties)runProperties.Clone());
                                wfldChar = new DOW.FieldChar();
                                wfldChar.FieldCharType = DOW.FieldCharValues.End;
                                run.Append(wfldChar);
                                wordParagraph.Append(run);

                            }
                            break;

                        case enumRunType.Image:
                            if (intRun.Image.Errors)
                            {
                                // Something is wrong with the Images so give author a hint
                                run = new DOW.Run(new DOW.Text(intRun.Image.ErrorMessage));
                                wordParagraph.Append(run);
                            }
                            else
                            {
                                Image img = intRun.Image;

                                // Save the image to the document. If it has been saved it wont be saved again. This must be called because
                                // SaveImage also puts the Image reference in the Documeny, Header or Footer whichever is appropriate
                                SaveImage(img, UniqueId);

                                //Create a drawing with the image
                                run = new DOW.Run(CreateDrawing(img));

                                wordParagraph.Append(run);
                            }

                            break;

                        case enumRunType.Break:
                            DOW.Break w_br = new DOW.Break() { Type = (DOW.BreakValues)intRun.BreakValue };
                            if (intRun.BreakValue == enumBreakValues.TextWrapping)
                                w_br.Clear = DOW.BreakTextRestartLocationValues.All;
                            run = new DOW.Run(w_br);
                            wordParagraph.Append(run);
                            break;

                        case enumRunType.Symbol:
                            int temp = (int)intRun.Symbol;

                            run = new DOW.Run(new DOW.SymbolChar() { Font = intRun.SymbolFont, Char = new HexBinaryValue(temp.ToString("x4")) });
                            wordParagraph.Append(run);
                            break;

                        default:
                            break;
                    }
                }
            }


            // Add Paragraph Style first as defined by ECMA documentation
            if (ThisParagraph.Style != null)
            {
                // Has the Style been saved
                if (ThisParagraph.Style.Id == null || ThisParagraph.Style.Id == "")
                    SaveStyle(ThisParagraph.Style);

                DOW.ParagraphStyleId wpStyle = new DOW.ParagraphStyleId() { Val = ThisParagraph.Style.Id };
                paragraphProperties.Append(wpStyle);
            }

            // do the paragraph properties
            AddFormatting(paragraphProperties, ThisParagraph);
            
           
            


            runProperties = new DOW.RunProperties();

            if (runProperties != null)
            {
                paragraphProperties.Append(runProperties);
            }

            wordParagraph.PrependChild(paragraphProperties);

            return wordParagraph;
        }

        /// <summary>
        /// Provides Paragraph formatting for Paragraphs and Styles
        /// </summary>
        /// <param name="ThisElement"></param>
        /// <param name="ThisParagraph"></param>
        private void AddFormatting(OpenXmlCompositeElement ThisElement, BaseParagraphFormatting ThisParagraph)
        {
            // The Properties are added in the proper sequence here as defined by the ECMA documentation
            // Note some properties are not used here

            Paragraph p = null;
            if (typeof(Paragraph) == ThisParagraph.GetType())
                p = (Paragraph)ThisParagraph;
            
            // Page break before can only happen on a paragraph
            if (p != null && p.PageBreakBefore)   
            {
                DOW.PageBreakBefore w_pageBreakBefore = new DOW.PageBreakBefore();
                ThisElement.Append(w_pageBreakBefore);
            }

            // Number properties w_numPr is not null if this is part of numbered (bullet) list
            if (w_numPr != null)
                ThisElement.Append(w_numPr);

            // Spacing
            if (ThisParagraph.LineSpacing != 0 || ThisParagraph.SpacingBefore != 0 || ThisParagraph.SpacingAfter != 0)
            {
                DOW.SpacingBetweenLines w_spacing = new DOW.SpacingBetweenLines();
                w_spacing.LineRule = ThisParagraph.LineSpacingRule;
                w_spacing.After = "0"; 

                if (ThisParagraph.LineSpacing != 0 ) w_spacing.Line = ThisParagraph.LineSpacing.ToString();
                if (ThisParagraph.SpacingBefore != 0) w_spacing.Before = ThisParagraph.SpacingBefore.ToString();
                if (ThisParagraph.SpacingAfter != 0) w_spacing.After = ThisParagraph.SpacingAfter.ToString();
                ThisElement.Append(w_spacing);
            }

            // Indentation
            if (ThisParagraph.LeftIndentation != 0 || ThisParagraph.RightIndentation != 0 || ThisParagraph.FirstLineIndentation != 0 || ThisParagraph.HangingIndentation != 0)
            {
                DOW.Indentation w_ind = new DOW.Indentation();
                if (ThisParagraph.LeftIndentation != 0) w_ind.Left = ThisParagraph.LeftIndentation.ToString();
                if (ThisParagraph.RightIndentation != 0) w_ind.Right = ThisParagraph.RightIndentation.ToString();

                // FirstLineIndentation and HangingIndention are mutually exclusive.
                if (ThisParagraph.FirstLineIndentation != 0 ) w_ind.FirstLine = ThisParagraph.FirstLineIndentation.ToString();
                if (ThisParagraph.HangingIndentation != 0) w_ind.Hanging = ThisParagraph.HangingIndentation.ToString();

                ThisElement.Append(w_ind);
            }

            // ContextualSpacing
            if (ThisParagraph.NoSpaceBetweenParagraphsOfSameStyle != null && (bool)ThisParagraph.NoSpaceBetweenParagraphsOfSameStyle)
            {
                ThisElement.Append(new DOW.ContextualSpacing()); // Default value for ContextualSpacing is on
            }

            // Justification
            if (ThisParagraph.Alignment != DOW.JustificationValues.Left)
                ThisElement.Append(new DOW.Justification() { Val = ThisParagraph.Alignment });
        }


        /// <summary>
        /// Provide MultipleStrings formatting for Styles
        /// </summary>
        /// <param name="CurrentStyleRunProperties"></param>
        /// <param name="TextFormat"></param>
        private void AddFormatting(DOW.StyleRunProperties CurrentStyleRunProperties, BaseTextFormatting TextFormat)
        {
            // rFonts
            if (TextFormat.FontName != null)
                CurrentStyleRunProperties.Append(new DOW.RunFonts() { Ascii = TextFormat.FontName });

            // b
            if (TextFormat.Bold != null)
                if ((bool)TextFormat.Bold)
                    CurrentStyleRunProperties.Append(new DOW.Bold());
                else
                    CurrentStyleRunProperties.Append(new DOW.Bold() { Val = new OnOffValue(false) });
            // i
            if (TextFormat.Italics != null)
                if( (bool)TextFormat.Italics)
                    CurrentStyleRunProperties.Append(new DOW.Italic());
                else
                    CurrentStyleRunProperties.Append(new DOW.Italic() { Val = new OnOffValue(false) });
            // vanish
            if (TextFormat.Hidden != null && (bool)TextFormat.Hidden)
                CurrentStyleRunProperties.Append(new DOW.Hidden());

            // color
            if (TextFormat.FontColour != "")
                if (TextFormat.FontColour != "")
                    CurrentStyleRunProperties.Append(new DOW.Color() { Val = TextFormat.FontColour });

            // sz
            if (TextFormat.FontSize != 0)
                CurrentStyleRunProperties.Append(new DOW.FontSize() { Val = (TextFormat.FontSize * 2).ToString() }); 

            // u
            if (TextFormat.UnderLine != null )
            {
                DOW.Underline w_u = new DOW.Underline();
                w_u.Val = TextFormat.UnderLineStyle;
                if (TextFormat.UnderLineColour != "")
                    w_u.Color = TextFormat.UnderLineColour;
                  
                CurrentStyleRunProperties.Append(w_u);
            }
        }


        /// <summary>
        /// Provides MultipleStrings formatting for Runs within Paragraps, by applying the Run MultipleStrings Format or if no Run MultipleStrings Format then the Paragraph MultipleStrings Format
        /// </summary>
        /// <param name="CurrentRunProperties"></param>
        /// <param name="CurrentRun"></param>
        /// <param name="SecondaryFormat"></param>
        private void AddFormatting(DOW.RunProperties CurrentRunProperties, Run CurrentRun, Paragraph SecondaryFormat)
        {
            // rFonts
            if (CurrentRun.FontName != null)
                CurrentRunProperties.Append(new DOW.RunFonts() { Ascii = CurrentRun.FontName });
            else if (SecondaryFormat.FontName != null)
                if (CurrentRun.FontName == null)
                    CurrentRunProperties.Append(new DOW.RunFonts() { Ascii = SecondaryFormat.FontName });

            // b
            if (CurrentRun.Bold != null)
                if ( (bool)CurrentRun.Bold)
                    CurrentRunProperties.Append(new DOW.Bold());
                else
                    CurrentRunProperties.Append(new DOW.Bold() { Val = new OnOffValue(false) });
            else if (SecondaryFormat.Bold != null)
                if ((bool)SecondaryFormat.Bold )
                    CurrentRunProperties.Append(new DOW.Bold());

            // i
            if (CurrentRun.Italics != null)
                if ((bool)CurrentRun.Italics)
                    CurrentRunProperties.Append(new DOW.Italic());
                else
                    CurrentRunProperties.Append(new DOW.Italic() { Val = new OnOffValue(false) });
            else if (SecondaryFormat.Italics != null)
                if ((bool)SecondaryFormat.Italics)
                    CurrentRunProperties.Append(new DOW.Italic());
            
            // vanish
            if (CurrentRun.Hidden != null && (bool)CurrentRun.Hidden)
                CurrentRunProperties.Append(new DOW.Hidden());

            // color
            if (CurrentRun.FontColour != "")
                CurrentRunProperties.Append(new DOW.Color() { Val = CurrentRun.FontColour });
            else if (SecondaryFormat.FontColour != "")
                if (SecondaryFormat.FontColour != "")

            // u
            if (CurrentRun.UnderLine != null)
            {
                DOW.Underline w_u = new DOW.Underline();
                w_u.Val = CurrentRun.UnderLineStyle;
                if (CurrentRun.UnderLineColour != "")
                    w_u.Color = CurrentRun.UnderLineColour;
                    
                CurrentRunProperties.Append(w_u);
            }

         

           
            
          

            if (CurrentRun.FontSize != 0)
            {
                CurrentRunProperties.Append(new DOW.FontSize() { Val = (CurrentRun.FontSize * 2).ToString() });
                CurrentRunProperties.Append(new DOW.FontSizeComplexScript() { Val = (CurrentRun.FontSize * 2).ToString() });
            }
            else if (SecondaryFormat.FontSize != 0)
            {
                if (CurrentRun.FontSize == 0)
                {
                    CurrentRunProperties.Append(new DOW.FontSize() { Val = (SecondaryFormat.FontSize * 2).ToString() });
                    CurrentRunProperties.Append(new DOW.FontSizeComplexScript() { Val = (SecondaryFormat.FontSize * 2).ToString() });
                }
            }
        }

        private DOW.Drawing CreateDrawing(Image img)
        {
            // generate a new Id
            GetNextId();
            uint nextId = (uint) IdCount;
            // If there is a border then we need to extend the drawing
            DWP.EffectExtent wr_effectExtent = new DWP.EffectExtent();
            if (img.BorderColour != "")
            {
                // * 10 is basically what MS Word 2013 does with these values
                wr_effectExtent.TopEdge = Document.pointsToEMUs(img.BorderWidth) * 10;
                wr_effectExtent.LeftEdge = Document.pointsToEMUs(img.BorderWidth) * 10;
                wr_effectExtent.BottomEdge = Document.pointsToEMUs(img.BorderWidth) * 10;
                wr_effectExtent.RightEdge = Document.pointsToEMUs(img.BorderWidth) * 10;
            }
            else
            {
                // Default effectExtent same as MS Word 2013
                wr_effectExtent.TopEdge = Document.pointsToEMUs(0);
                wr_effectExtent.LeftEdge = Document.pointsToEMUs(0);
                wr_effectExtent.BottomEdge = Document.pointsToEMUs(01);
                wr_effectExtent.RightEdge = Document.pointsToEMUs(0.5);
            }
            return new DOW.Drawing(
                new DWP.Inline(

                    new DWP.Extent()
                    {
                        Cx = img.OutputWidth,
                        Cy = img.OutputHeight
                    },
                    wr_effectExtent,
                    
                    new DWP.DocProperties()
                    {
                        Id = nextId,
                        Name = "Inline MultipleStrings Wrapping Picture",
                        Description = img.FileName
                    },

                    new DWP.NonVisualGraphicFrameDrawingProperties(
                    new DRAW.GraphicFrameLocks() { NoChangeAspect = true }),

                    new DRAW.Graphic(
                    new DRAW.GraphicData(
                        CreatePicture(img)
                    ) { Uri = GraphicDataUri })
                )
                {
                    DistanceFromTop = (UInt32Value)0U,
                    DistanceFromBottom = (UInt32Value)0U,
                    DistanceFromLeft = (UInt32Value)0U,
                    DistanceFromRight = (UInt32Value)0U
                }
            );
        }
       

        private PIC.Picture CreatePicture(Image img)
        {
            PIC.Picture pic = new PIC.Picture(
                new PIC.NonVisualPictureProperties(
                    new PIC.NonVisualDrawingProperties()
                    {
                        Id = (UInt32Value)0U,
                        Name = img.FileName
                    },
                    new PIC.NonVisualPictureDrawingProperties()),

                new PIC.BlipFill(
                    new DRAW.Blip() { Embed = img.ImageId },
                    new DRAW.Stretch(
                    new DRAW.FillRectangle()))

                
           );
        
            // Create the Shape
            PIC.ShapeProperties pic_spPr =
                new PIC.ShapeProperties(
                    new DRAW.Transform2D(
                        new DRAW.Offset() { X = 0L, Y = 0L },
                        new DRAW.Extents()
                        {
                            Cx = img.OutputWidth,
                            Cy = img.OutputHeight
                        }
                    ),

                    new DRAW.PresetGeometry(
                        new DRAW.AdjustValueList()
                    ) { Preset = DRAW.ShapeTypeValues.Rectangle }
                );

          
            // Add an Outline to the Shape if needed
            if (img.BorderColour != "")
            {
                HexBinaryValue hbv = null;
                hbv = new HexBinaryValue(img.BorderColour);

                pic_spPr.Append(
                    new DRAW.Outline(
                        new DRAW.SolidFill(
                            new DRAW.RgbColorModelHex() {Val = hbv}
                            )
                        ) { Width = (int) Document.pointsToEMUs(img.BorderWidth) * 10}
                    );
            }

            pic.Append(pic_spPr);

            return pic;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ThisTable"></param>
        /// <param name="MergeMap"></param>
        /// <param name="RowIndex"></param>
        /// <param name="ColumnIndex"></param>
        /// <param name="RowSpan">Either this or ColumnSpan greater than 1</param>
        /// <param name="ColumnSpan">Either this or RowSpan greater than 1</param>
        private void FillMergeMap( Table ThisTable, char[,] MergeMap, int RowIndex, int ColumnIndex, int RowSpan, int ColumnSpan)
        {
            // Make sure row span wont take us outside of the Table
            int rowsToTableEnd = ThisTable.Rows.Count - RowIndex;
            rowsToSpan = RowSpan < rowsToTableEnd ? RowSpan : rowsToTableEnd;

            // Make sure column span wont take us outside of the Table
            int colsToTableEnd = ThisTable.Columns.Count - ColumnIndex;
            columnsToSpan =  ColumnSpan < colsToTableEnd ? ColumnSpan : colsToTableEnd;

            // Scan this row to see if we "clash" with other merged cells. At the end of this for loop
            // we can gaurantee that columnsToSpan won't clash with another merged area
            for (int i = 1; i < columnsToSpan; i++) // i =1 because we would be here if this cell had a merge conflict
            {
                if (MergeMap[RowIndex, ColumnIndex + i] != '\0')
                {
                    // Bumped into someother merged cells
                    columnsToSpan = i; 
                    break;
                }
            }

            if (RowSpan > 1) // Row spanned and possible columns as well
            {
                for (int r = 0; r < rowsToSpan; r++)
                {
                    mergeMap[RowIndex + r, ColumnIndex] = ROWSPANCONTINUE;
                    ThisTable[RowIndex + r][ColumnIndex].ColumnSpan = columnsToSpan; // Fix the actual ColumnSpan figure
                    for (int c = 1; c < columnsToSpan; ++c)
                    {
                        mergeMap[RowIndex + r, ColumnIndex + c] = CELLSPANNEDIGNORE;
                    }
                }
                // Fix up first row
                mergeMap[RowIndex, ColumnIndex] = ROWSPANSTART;
            }
            else
            {
                // Column span only
                mergeMap[RowIndex, ColumnIndex] = COLUMNONLYSPANNED; // Fix the actual ColumnSpan figure
                ThisTable[RowIndex][ColumnIndex].ColumnSpan = columnsToSpan;
                for (int i = 1; i < columnsToSpan; ++i)
                    mergeMap[RowIndex, ColumnIndex + i] = CELLSPANNEDIGNORE;
            }
        }
        /// <summary>
        /// Create an OOXML Table from a Tatibah.Table, it could then be Appeded to the CurrentBody. When serialize the qualified name is w:tbl
        /// </summary>
        /// <param name="ThisTable"></param>
        /// <returns></returns>
        public DocumentFormat.OpenXml.Wordprocessing.Table CreateWordTable(Table ThisTable, string UniqueId)
        {
            DocumentFormat.OpenXml.Wordprocessing.Table table = new DocumentFormat.OpenXml.Wordprocessing.Table();
            DocumentFormat.OpenXml.Wordprocessing.Paragraph para  = null;

            DOW.TableCellProperties tableCellProperties = null;
            // Add the border, the default is a single border
            string borderColour = "000000"; // Black
            uint borderWidth = 6;

            DOW.TableProperties w_tblPr = new DOW.TableProperties();

            // The Properties are added in the proper sequence here as defined by the ECMA documentation
            // Note some ECMA properties are not used here

            // TableWidth
            if (ThisTable.Width == 0)
                w_tblPr.Append(new DOW.TableWidth() { Width = ThisTable.Width.ToString(), Type = DOW.TableWidthUnitValues.Auto });
            else
                w_tblPr.Append(new DOW.TableWidth() { Width = ThisTable.Width.ToString(), Type = ThisTable.WidthType });

            // TableJustification
            if (ThisTable.Alignment != DOW.TableRowAlignmentValues.Left)
                w_tblPr.Append(new DOW.TableJustification() { Val = ThisTable.Alignment });

            DOW.TableBorders w_tblBorders = new DOW.TableBorders();

            // TopBorder
            if (ThisTable.TopBorder != DOW.BorderValues.None)
            {
                if (ThisTable.TopBorderColour == "")
                    borderColour = "000000"; //Black
                else
                    borderColour = ThisTable.TopBorderColour;

                if (ThisTable.TopBorderWidth == 0)
                    borderWidth = 6;
                else
                    borderWidth = ThisTable.TopBorderWidth;

                w_tblBorders.Append(
                    new DOW.TopBorder()
                    {
                        Val = ThisTable.TopBorder,
                        Color = borderColour,
                        Size = borderWidth
                    }
                    );
            }

            // LeftBorder
            if (ThisTable.LeftBorder != DOW.BorderValues.None)
            {
                if (ThisTable.LeftBorderColour == "")
                    borderColour = "000000"; //Black
                else
                    borderColour = ThisTable.LeftBorderColour;

                if (ThisTable.LeftBorderWidth == 0)
                    borderWidth = 6;
                else
                    borderWidth = ThisTable.LeftBorderWidth;

                w_tblBorders.Append(
                    new DOW.StartBorder()
                    {
                        Val = ThisTable.LeftBorder,
                        Color = borderColour,
                        Size = borderWidth
                    }
                    );
            }

            // Bottom Border
            if (ThisTable.BottomBorder != DOW.BorderValues.None)
            {
                if (ThisTable.BottomBorderColour == "")
                    borderColour = "000000"; //Black
                else
                    borderColour = ThisTable.BottomBorderColour;

                if (ThisTable.BottomBorderWidth == 0)
                    borderWidth = 6;
                else
                    borderWidth = ThisTable.BottomBorderWidth;

                w_tblBorders.Append(
                    new DOW.BottomBorder()
                    {
                        Val = ThisTable.BottomBorder,
                        Color = borderColour,
                        Size = borderWidth
                    }
                    );
            }

            // RightBorder
            if (ThisTable.RightBorder != DOW.BorderValues.None)
            {
                if (ThisTable.RightBorderColour == "")
                    borderColour = "000000"; //Black
                else
                    borderColour = ThisTable.RightBorderColour;

                if (ThisTable.RightBorderWidth == 0)
                    borderWidth = 6;
                else
                    borderWidth = ThisTable.RightBorderWidth;

                w_tblBorders.Append(
                    new DOW.EndBorder()
                    {
                        Val = ThisTable.RightBorder,
                        Color = borderColour,
                        Size = borderWidth
                    }
                    );
            }

            // InsideHorizontalBorder
            if (ThisTable.InsideHorizontalBorder != DOW.BorderValues.None)
            {
                if (ThisTable.InsideHorizontalBorderColour == "")
                    borderColour = "000000"; //Black
                else
                    borderColour = ThisTable.InsideHorizontalBorderColour;

                if (ThisTable.InsideHorizontalBorderWidth == 0)
                    borderWidth = 6;
                else
                    borderWidth = ThisTable.InsideHorizontalBorderWidth;

                w_tblBorders.Append(
                    new DOW.InsideHorizontalBorder()
                    {
                        Val = ThisTable.InsideHorizontalBorder,
                        Color = borderColour,
                        Size = borderWidth
                    }
                    );
            }

            // InsideVerticalBorder
            if (ThisTable.InsideVerticalBorder != DOW.BorderValues.None)
            {
                if (ThisTable.InsideVerticalBorderColour == "")
                    borderColour = "000000"; //Black
                else
                    borderColour = ThisTable.InsideVerticalBorderColour;

                if (ThisTable.InsideVerticalBorderWidth == 0)
                    borderWidth = 6;
                else
                    borderWidth = ThisTable.InsideVerticalBorderWidth;

                w_tblBorders.Append(
                    new DOW.InsideVerticalBorder()
                    {
                        Val = ThisTable.InsideVerticalBorder,
                        Color = borderColour,
                        Size = borderWidth
                    }
                    );
            }

            w_tblPr.Append(w_tblBorders);

            // TableLayout
            if (ThisTable.TableLayout != DOW.TableLayoutValues.Autofit)
                w_tblPr.Append(
                    new DOW.TableLayout() { Type = ThisTable.TableLayout }
                    );

            // Add a Table Style Conditional Formatting Setting Exception $17.4.55
            w_tblPr.Append(
                new DOW.TableLook() 
                {
                    FirstRow = new OnOffValue(false),
                    LastRow = new OnOffValue(false),
                    FirstColumn = new OnOffValue(false),
                    LastColumn = new OnOffValue(false),
                    NoHorizontalBand = new OnOffValue(false),
                    NoVerticalBand = new OnOffValue(false)
                }
            );

            // Add mandatory Properties to the table
            table.Append(w_tblPr);

            // Add mandatory Table Grid 
            DOW.TableGrid w_tblGrid = new DOW.TableGrid();

            // Scan the columns widths
            uint columnsWidths = 0;
            for (int i = 0; i < ThisTable.Columns.Count; i++)
            {
                columnsWidths += ThisTable.Columns[i].Width;
            }
            double missingColumnWidth = Math.Floor(( (double)PageWidth - (double)columnsWidths) / (double)ThisTable.Columns.Count);

            foreach (Column item in ThisTable.Columns)
            {
                if (item.Width == 0)
                    w_tblGrid.Append(new DOW.GridColumn() { Width = missingColumnWidth.ToString() });
                else
                    w_tblGrid.Append( new DOW.GridColumn() { Width = item.Width.ToString() } );
            }
            table.Append(w_tblGrid);

            // Loop through Rows and Cells
            DOW.TableRow tr = null;
            DOW.TableCell tc = null;
            mergeMap = null;
            Row row = null;
            for (int rowIndex = 0; rowIndex < ThisTable.Rows.Count; ++rowIndex )
            {
                row = ThisTable[rowIndex];
                tr = new DOW.TableRow();
                for (int cellIndex = 0; cellIndex < row.Cells.Count; ++cellIndex)
                {
                    Cell cell = row.Cells[cellIndex];
                    tc = new DOW.TableCell();
                    tableCellProperties = new DOW.TableCellProperties();

                    // Add any Cell Properties order specified by the ECMA documentation

                    // Cell width - Preferred Value
                    if (cell.Width != 0)
                        tableCellProperties.Append(new DOW.TableCellWidth() { Width = cell.Width.ToString(), Type = cell.WidthType });
                    else
                        if (ThisTable.Columns[cellIndex].Width != 0)
                            tableCellProperties.Append(new DOW.TableCellWidth()
                            {
                                Width = ThisTable.Columns[cellIndex].Width.ToString(),
                                Type = DOW.TableWidthUnitValues.Dxa // Columns widths can only be in DXAs 
                            });
                        else
                        {
                            tableCellProperties.Append(new DOW.TableCellWidth()
                            {
                                Width = missingColumnWidth.ToString(),
                                Type = DOW.TableWidthUnitValues.Dxa // Columns widths can only be in DXAs 
                            });
                        }

                    // RowSan or CellSpan = 1 means no spanning
                    if (cell.RowSpan > 1 || cell.ColumnSpan > 1)
                        if (mergeMap == null || mergeMap[rowIndex, cellIndex] == '\0')
                        {
                            //Make sure there are no merge clashes
                            if (mergeMap == null) mergeMap = new char[ThisTable.Rows.Count, ThisTable.Columns.Count];
                            FillMergeMap(ThisTable, mergeMap, rowIndex, cellIndex, (int)cell.RowSpan, (int)cell.ColumnSpan);
                        }

                    if (mergeMap != null)
                    {
                        if (mergeMap[rowIndex, cellIndex] == CELLSPANNEDIGNORE)
                            continue;
                        else if (mergeMap[rowIndex, cellIndex] == ROWSPANSTART)  // Start of a Row Span
                        {
                            if (cell.ColumnSpan > 1)
                                tableCellProperties.Append(new DOW.GridSpan() { Val = cell.ColumnSpan });
                            tableCellProperties.Append(new DOW.VerticalMerge() { Val = DOW.MergedCellValues.Restart });
                        }
                        else if (mergeMap[rowIndex, cellIndex] == ROWSPANCONTINUE)
                        {
                            if (cell.ColumnSpan > 1)
                                tableCellProperties.Append(new DOW.GridSpan() { Val = cell.ColumnSpan });
                            tableCellProperties.Append(new DOW.VerticalMerge()); // default is Continue
                        }
                        else if (mergeMap[rowIndex, cellIndex] == COLUMNONLYSPANNED)
                        {
                            tableCellProperties.Append(new DOW.GridSpan() { Val = cell.ColumnSpan });
                        }
                    }


                    // Background colour Shading
                    if (cell.BackgroundColour != "")
                        tableCellProperties.Append(new DOW.Shading() { Val = DOW.ShadingPatternValues.Clear, Color = "auto", Fill = cell.BackgroundColour });

                    // TableCellVerticalAlignment
                    if (cell.VerticalAlignment != DOW.TableVerticalAlignmentValues.Top)
                        tableCellProperties.Append(new DOW.TableCellVerticalAlignment() { Val = cell.VerticalAlignment });

                    // Add the Cell Properties
                    tc.Append(tableCellProperties);

                    if (cell.Contents.Count == 0)
                    {
                        // No content in cell so add emply paragraph
                        para = new DocumentFormat.OpenXml.Wordprocessing.Paragraph();
                        tc.Append(para);
                    }
                    else
                    {
                        CellContent content;
                        for (int i = 0; i < cell.Contents.Count; ++i)
                        {
                            content = cell.Contents[i];
                            switch (content.ContentType)
                            {
                                case enumCellContentType.Paragraph:
                                    para = CreateWordParagraph(content.Paragraph, UniqueId);
                                    tc.Append(para);
                                    break;

                                case enumCellContentType.NumberedList:

                                    SaveNumberedListFormat(content.NumberedList);

                                    foreach (Paragraph p in (content.NumberedList).listOfParagraphs)
                                    {
                                        // Step 1. Create a normal OOXML paragraph
                                        DocumentFormat.OpenXml.Wordprocessing.Paragraph wordPara = CreateWordParagraph(p, UniqueId);
                                        // Step 2. Get the above paragraph's ParagraphProperties
                                        DOW.ParagraphProperties w_pPr = (DOW.ParagraphProperties)wordPara.FirstChild; // Since we always add Paragraph Properties this must be a Paragraph Property

                                        // Step 3 Add the numbering elements to the above ParagraphProperties
                                        w_pPr.Append(new DOW.NumberingProperties(
                                              new DOW.NumberingLevelReference() { Val = 0 },
                                              new DOW.NumberingId() { Val = (content.NumberedList).LevelId + 1 }
                                              ));

                                        tc.Append(wordPara);
                                    }

                                    break;

                                case enumCellContentType.Table:
                                    DocumentFormat.OpenXml.Wordprocessing.Table tab = CreateWordTable(content.Table, UniqueId);
                                    tc.Append(tab);

                                    // A table MUST be followed by a paragraph
                                    if (i == cell.Contents.Count - 1)
                                    {
                                        para = new DocumentFormat.OpenXml.Wordprocessing.Paragraph();
                                        tc.Append(para);
                                    }
                                    break;

                                case enumCellContentType.Image:

                                    if (cell.Contents[i].Image.Errors)
                                    {
                                        para = new DocumentFormat.OpenXml.Wordprocessing.Paragraph(
                                            new DOW.Run(
                                                new DOW.Text(cell.Contents[i].Image.ErrorMessage)));
                                        tc.Append(para);
                                    }
                                    else
                                    {
                                        Image img = content.Image;
                                        // Save the image to the document. If it has an ImageID then it has already been saved
                                        DOW.Run run = null;
                                        if (img.ImageId == null || img.ImageId == "")
                                        {
                                            SaveImage(img, UniqueId);
                                            if (img.Errors)
                                                run = new DOW.Run(new DOW.Text("Image error: " + img.ErrorMessage));
                                            else
                                                run = new DOW.Run(CreateDrawing(img));
                                        }
                                        else
                                            run = new DOW.Run(CreateDrawing(img));
                                        para = new DocumentFormat.OpenXml.Wordprocessing.Paragraph();
                                        para.Append(run);
                                        tc.Append(para);
                                    }
                                    break;
                                default:
                                    // Not sure what it is so add an empty paragraph
                                    para = new DocumentFormat.OpenXml.Wordprocessing.Paragraph();
                                    tc.Append(para);
                                    break;
                            }
                        }
                    }
                    tr.Append(tc);
                }
                table.Append(tr);
            }
            
            return table;
        }


        /// <summary>
        /// Save The Word Document
        /// </summary>
        public void Save()
        {
            if (saved) return; // can't save twice

            AddSectionProperties(this, documentSectionProperties);

            // Save the numbered list if ther are any
            if (w_numbering != null)
            {
                foreach (DOW.AbstractNum num in abstractNumberList)
                {
                    w_numbering.Append(num);
                }

                for (int i = 0; i < abstractNumberList.Count; i++)
                {
                    w_numbering.Append(new DOW.NumberingInstance(new DOW.AbstractNumId() { Val = new Int32Value(i) }) { NumberID = new Int32Value(i + 1) });
                }
                w_numbering.Save(numberingDefinitionPart);
            }
            CurrentBody.Append(documentSectionProperties);

           
            // Create the Setting Part
            documentSettingsPart = mainPart.AddNewPart<DocumentSettingsPart>();
            documentSetting = new DOW.Settings();
            documentSetting.Append(new DOW.DefaultTabStop() { Val = 720 }); // Word default
            if (w_evenAndOddHeaders != null)
                documentSetting.Append(w_evenAndOddHeaders);
            documentSetting.Save(documentSettingsPart);  // Save the document settings
            
            mainPart.Document.Append(CurrentBody);
            mainPart.Document.Save();
            saved = true;
        }

        /// <summary>
        ///  Close The Document
        /// </summary>
        public void Close()
        {
            // Save document if autosave is true
            if (Autosave && !saved) Save();

            // Dispose if called first will close the Document
            if (!closed) document.Close();
            closed = true;

            // Need to dispose of resources
            if (!disposed) Dispose();
          
        }
  
         /// <summary>
        /// Save the Image internally and set the Height and Width
        /// </summary>
        /// <param name="ThisImage"></param>
        public  void SaveImage(Image ThisImage)
        {
            SaveImage(ThisImage, UNIQUEID);
        }

        /// <summary>
        /// Save the Image internally and set the Height and Width
        /// </summary>
        /// <param name="ThisImage"></param>
        private void SaveImage(Image ThisImage, string UniqueId)
        {
            byte[] imageFileBytes = null;
            SD.Bitmap imageFile = null;
         
            string imageId = "";

            if (ThisImage.ImageId == null || ThisImage.ImageId == "")
                ThisImage.ReservedI(GetNextId());
            imageId = ThisImage.ImageId;

            // if currentHeaderOrFooterPart is NOT null then we are processing a header or footer, so
            // add the Imge to the appropriate part. If is null then must be added to the body
            if (currentHeaderOrFooterPart != null)
            {
                if (!imageInPart.ContainsKey(imageId + UniqueId))
                {
                    imagePart = currentHeaderOrFooterPart.AddNewPart<ImagePart>("image/jpeg", imageId);
                    imageInPart.Add(imageId + UniqueId, 0); // we don't care about the value
                }
              
            }
            else
            {
                if (!imageInPart.ContainsKey(imageId + UniqueId))
                {
                    imagePart = mainPart.AddNewPart<ImagePart>("image/jpeg", imageId);
                    imageInPart.Add(imageId + UniqueId, 0); // we don't care about the value
                }
               
            }

           

            if (ThisImage.ImageArray == null) // We don't already have a byte array for the image so lets get one
            {
                try
                {
                    // Open a stream on the image file and read it's contents.
                    using (FileStream fsImageFile = File.OpenRead(ThisImage.FileName))
                    {
                        imageFileBytes = new byte[fsImageFile.Length];
                        fsImageFile.Read(imageFileBytes, 0, imageFileBytes.Length);

                        imageFile = new SD.Bitmap(fsImageFile);
                    }
                }
                catch (Exception exp)
                {
                    ThisImage.Errors = true;
                    ThisImage.ErrorMessage = exp.Message;
                    return;
                }
            }
            else
            {
                MemoryStream msImageFile = new MemoryStream(ThisImage.ImageArray);
                imageFileBytes = new byte[ThisImage.ImageArray.Length];
                msImageFile.Read(imageFileBytes, 0, ThisImage.ImageArray.Length);
                imageFile = new SD.Bitmap(msImageFile);
            }
     
            // Get the dimensions of the image in English Metric Units (EMU)
            // for use when adding the markup for the image to the document.
            ThisImage.ReservedH((long)((imageFile.Height / imageFile.VerticalResolution) * 914400L), (long)((imageFile.Width / imageFile.HorizontalResolution) * 914400L));
            // Write the contents of the image to the ImagePart.
 
            using (BinaryWriter writer = new BinaryWriter(imagePart.GetStream()))
            {
                writer.Write(imageFileBytes);
                writer.Flush();
            }
        }

        #region H E L P E R   F UN C T I O N S


        public static string GetNextId()
        {
            return ID + (++IdCount);
        }

        private string GetCurrentId()
        {
            return ID + (IdCount);
        }

        private string GetNextHexId()
        {
            return (++hexCount).ToString("X2");
        }


        #endregion  H E L P E R   F UN C T I O N S

        #region S T A T I C   F U N C T I O N S
        /// <summary>
        /// Converts the paramater Value of Units to points
        /// </summary>
        /// <param name="Value">The value to be converted</param>
        /// <returns></returns>
        public static uint pointsToDxa(Double Value)
        {
            double tmp = Value * PointToDxa;
            uint Int = (uint)Math.Ceiling(tmp);
            return Int;
        }


        /// <summary>
        /// Convert points to EMU units
        /// </summary>
        /// <param name="Value"></param>
        /// <returns></returns>
        public static uint pointsToEMUs(Double Value)
        {
            double tmp = Value * PointToEmus;
            uint Int = (uint)Math.Ceiling(tmp);
            return Int;
        }

        /// <summary>
        /// Convert inched to DXA units
        /// </summary>
        /// <param name="Value"></param>
        /// <returns></returns>
        public static uint inchsToDxa(Double Value)
        {
            double tmp = Value * InchToDxa;
            uint Int = (uint)Math.Ceiling(tmp);
            return Int;
        }

        /// <summary>
        /// Convert inches to EMU units
        /// </summary>
        /// <param name="Value"></param>
        /// <returns></returns>
        public static uint inchsToEMUs(Double Value)
        {
            double tmp = Value * InchToEmus;
            uint Int = (uint)Math.Ceiling(tmp);
            return Int;
        }

        /// <summary>
        /// Convert centimemters to DXA units
        /// </summary>
        /// <param name="Value"></param>
        /// <returns></returns>

        public static uint cmsToDxa(Double Value)
        {
            double tmp = Value * CMtoDxa;
            uint Int = (uint)Math.Round(tmp);
            return Int;
        }

        /// <summary>
        /// Convert centimemters to EMU units
        /// </summary>
        /// <param name="Value"></param>
        /// <returns></returns>

        public static uint cmsToEMUs(Double Value)
        {
            double tmp = Value * CMtoEmus;
            uint Int = (uint)Math.Ceiling(tmp);
            return Int;
        }
       
        /// <summary>
        /// Convert a System.Color to its Hex values
        /// </summary>
        /// <param name="Colour"></param>
        /// <returns></returns>
        public static string ColourToHex(SD.Color Colour)
        {
            return Colour.R.ToString("X2") + Colour.G.ToString("X2") + Colour.B.ToString("X2");
          
        }

        /// <summary>
        /// Internal function to add Headers and Footers to either a Document or a Section within a document
        /// </summary>
        /// <param name="HeaderOrFooter">The Header or Footer to be added</param>
        /// <param name="XmlHeaderOrFooter">The XML part to add too</param>
        /// <param name="ThisDocument">The document that needs the parts added too</param>
        private void AddHeaderFooterContent(BaseHeaderFooter HeaderOrFooter, DocumentFormat.OpenXml.OpenXmlPartRootElement XmlHeaderOrFooter, Document ThisDocument)
        {
            foreach (HeaderFooterContent content in HeaderOrFooter.Contents)
            {
                switch (content.ContentType)
                {
                    case enumHeaderFooterContentType.Paragraph:
                        DocumentFormat.OpenXml.Wordprocessing.Paragraph paragraph = ThisDocument.CreateWordParagraph(content.Paragraph, HeaderOrFooter.UniqueId);
                        XmlHeaderOrFooter.Append(paragraph);
                        break;

                    case enumHeaderFooterContentType.Table:
                        DocumentFormat.OpenXml.Wordprocessing.Table table = ThisDocument.CreateWordTable(content.Table, HeaderOrFooter.UniqueId);
                        XmlHeaderOrFooter.Append(table);
                        break;

                    case enumHeaderFooterContentType.NumberedList:
                        ThisDocument.SaveNumberedListFormat((NumberedList)content.NumberedList);

                        foreach (Paragraph p in ((NumberedList)content.NumberedList).listOfParagraphs)
                        {
                            // Step 1. Create a normal OOXML paragraph
                            DocumentFormat.OpenXml.Wordprocessing.Paragraph wordPara = ThisDocument.CreateWordParagraph(p, HeaderOrFooter.UniqueId);
                            // Step 2. Get the above paragraph's ParagraphProperties
                            DOW.ParagraphProperties w_pPr = (DOW.ParagraphProperties)wordPara.FirstChild; // Since we always add Paragraph Properties this must be a Paragraph Property

                            // Step 3 Add the numbering elements to the above ParagraphProperties
                            w_pPr.Append(new DOW.NumberingProperties(
                                  new DOW.NumberingLevelReference() { Val = 0 },
                                  new DOW.NumberingId() { Val = ((NumberedList)content.NumberedList).LevelId + 1 }
                                  ));

                            XmlHeaderOrFooter.Append(wordPara);
                        }
                        break;

                    case enumHeaderFooterContentType.None:
                        break;

                    default:
                        break;
                }
            }
        }

        /// <summary>
        /// Internally used function to add Section Properties to either a Dcoument or a Section within a Document
        /// </summary>
        /// <param name="Section"></param>
        /// <param name="XmlSection"></param>
        private void AddSectionProperties(BaseSectionFormatting Section, DOW.SectionProperties XmlSection)
        {
            // Set Page size. Default A4
            DOW.PageSize wpgSz = new DOW.PageSize();
            wpgSz.Width = Section.PageWidth;
            wpgSz.Height = Section.PageHeight;
            wpgSz.Orient = (DOW.PageOrientationValues)Section.PageOrientation;
            XmlSection.Append(wpgSz);

            // Set Margins
            DOW.PageMargin wpgMar = new DOW.PageMargin();
            wpgMar.Top = (int)Section.TopMargin;
            wpgMar.Bottom = (int)Section.BottomMargin;
            wpgMar.Left = Section.LeftMargin;
            wpgMar.Right = Section.RightMargin;
            wpgMar.Header = Section.HeaderMargin;
            wpgMar.Footer = Section.FooterMargin;
            XmlSection.Append(wpgMar);

            // TitlePage
            if (wtitlePg != null)
                XmlSection.Append(wtitlePg);
        }
       

        #endregion S T A T I C   F U N C T I O N S

    }
}
